/*
 * Copyright �2008 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 *
 */
package com.sun.lwuit.uidemo;

import com.sun.lwuit.Button;
import com.sun.lwuit.animations.CommonTransitions;
import com.sun.lwuit.Command;
import com.sun.lwuit.Component;
import com.sun.lwuit.Dialog;
import com.sun.lwuit.Display;
import com.sun.lwuit.Font;
import com.sun.lwuit.Form;
import com.sun.lwuit.Graphics;
import com.sun.lwuit.Image;
import com.sun.lwuit.Label;
import com.sun.lwuit.TextArea;
import com.sun.lwuit.animations.Transition;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.events.FocusListener;
import com.sun.lwuit.geom.Dimension;
import com.sun.lwuit.layouts.BorderLayout;
import com.sun.lwuit.layouts.BoxLayout;
import com.sun.lwuit.layouts.GridLayout;
import com.sun.lwuit.plaf.Style;
import com.sun.lwuit.plaf.UIManager;
import com.sun.lwuit.util.Resources;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
import javax.microedition.io.Connector;
import javax.microedition.io.InputConnection;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;

/**
 * Bootstraps the UI toolkit demos 
 *
 * @author Shai Almog
 */
public class UIDemoMIDlet extends javax.microedition.midlet.MIDlet implements ActionListener {
    private static final int EXIT_COMMAND = 1;
    private static final int RUN_COMMAND = 2;
    private static final int BACK_COMMAND = 3;
    private static final int ABOUT_COMMAND = 4;
    private static final int DRAG_MODE_COMMAND = 5;
    private static final int SCROLL_MODE_COMMAND = 6;
    private static final Command runCommand = new Command("Run", RUN_COMMAND);
    private static final Command exitCommand = new Command("Exit", EXIT_COMMAND);
    private static final Command backCommand = new Command("Back", BACK_COMMAND);
    private static final Command aboutCommand = new Command("About", ABOUT_COMMAND);
    private static final Command dragModeCommand = new Command("Drag Mode", DRAG_MODE_COMMAND);
    private static final Command scrollModeCommand = new Command("Scroll Mode", SCROLL_MODE_COMMAND);
    private static final Demo[] DEMOS = new Demo[]{
        new ThemeDemo(), new RenderingDemo(), new AnimationDemo(), new ButtonsDemo(),
        new TransitionDemo(), new FontDemo(), new TabbedPaneDemo(), new DialogDemo(), new LayoutDemo(), new ScrollDemo()
    };
    private Demo currentDemo;
    private static Transition componentTransitions;
    private Hashtable demosHash = new Hashtable();
    private static MainScreenForm mainMenu;
    private int cols;
    private int elementWidth;
    private static boolean localResources;
    private static final String[] RESOURCE_NAMES = {"resources", "duke", "javaTheme", "businessTheme", "starTheme", "oceanfishTheme", "images"};

    protected void startApp() {
        try {
            Display.init(this);
//            InputStream stream = getClass().getResourceAsStream("/res");
//            Resources r2;
//            if (stream == null) {
//                localResources = false;
//
//                try {
//                    RecordStore.openRecordStore(RESOURCE_NAMES[0], false).closeRecordStore();
//                    Resources r1 = getResource("javaTheme");
//                    UIManager.getInstance().setThemeProps(r1.getTheme(r1.getThemeResourceNames()[0]));
//
//                    setMainForm(UIDemoMIDlet.getResource("resources"));
//                    return;
//                } catch (Exception ignor) {
//                    // this exception is expected the first time the application is executed
//                }
//                downloadResources();
//                return;
//            } else {
//                localResources = true;
//                r2 = Resources.open(stream);
//                stream.close();
//            }

            //resources are built during the build process
            //open the build.xml file to figure out how to construct the 
            //resource files
            Resources r1 = Resources.open("/javaTheme.res");
            UIManager.getInstance().setThemeProps(r1.getTheme(r1.getThemeResourceNames()[0]));

            setMainForm();
                        
        } catch (Throwable ex) {
            ex.printStackTrace();
            Dialog.show("Exception", ex.getMessage(), "OK", null);
        }
    }

    /**
     * Downloads resources for the very first activation of the demo if it is running
     * with light deployment where the resources are not packaged in the JAR itself
     */
    private void downloadResources() {
        // download resources from the internet and install them in the RMS
        // while showing a progress indicator
        Form pleaseWait = new Form("Download");
        pleaseWait.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
        pleaseWait.addComponent(new Label("Downloading Resources"));
        final Label progressInfo = new Label("Starting");
        progressInfo.setAlignment(Component.CENTER);
        final Progress prog = new Progress();
        pleaseWait.addComponent(progressInfo);
        pleaseWait.addComponent(prog);
        pleaseWait.show();
        new Thread() {

            public void run() {
                try {
                    byte[] buffer = new byte[4096];
                    for (int iter = 0; iter < RESOURCE_NAMES.length; iter++) {
                        progressInfo.setText("Downloading: " + RESOURCE_NAMES[iter]);
                        byte percent = (byte) (100.0f * ((iter + 0.25f) / ((float) RESOURCE_NAMES.length)));
                        prog.setProgress(percent);
                        String url = getAppProperty(RESOURCE_NAMES[iter] + "-url");
                        InputConnection inputCon = (InputConnection) Connector.open(url, Connector.READ);
                        InputStream stream = inputCon.openInputStream();
                        ByteArrayOutputStream output = new ByteArrayOutputStream();
                        int size = stream.read(buffer);
                        while (size > -1) {
                            output.write(buffer, 0, size);
                            size = stream.read(buffer);
                        }
                        stream.close();
                        inputCon.close();
                        progressInfo.setText("Storing: " + RESOURCE_NAMES[iter]);
                        RecordStore store = RecordStore.openRecordStore(RESOURCE_NAMES[iter], true);
                        byte[] array = output.toByteArray();
                        store.addRecord(array, 0, array.length);
                        store.closeRecordStore();
                    }
                    progressInfo.setText("Done!");
                    prog.setProgress((byte) 100);
                    Resources r1 = getResource("javaTheme");
                    UIManager.getInstance().setThemeProps(r1.getTheme(r1.getThemeResourceNames()[0]));

                    setMainForm(/*UIDemoMIDlet.getResource("resources")*/);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    Dialog.show("Exception", ex.getMessage(), "OK", null);
                }
            }
        }.start();
    }

    /**
     * Used instead of using the Resources API to allow us to fetch locally downloaded
     * resources
     * 
     * @param name the name of the resource
     * @return a resources object
     */
    public static Resources getResource(String name) throws IOException {
        if (localResources) {
            return Resources.open("/" + name + ".res");
        }

        byte[] resourceData = null;
        try {
            resourceData = RecordStore.openRecordStore(name, false).enumerateRecords(null, null, false).nextRecord();
        } catch (RecordStoreException ex) {
            ex.printStackTrace();
            throw new IOException(ex.getMessage());
        }
        return Resources.open(new ByteArrayInputStream(resourceData));
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean arg0) {
    }

    public static void setTransition(Transition in, Transition out) {
        mainMenu.setTransitionInAnimator(in);
        mainMenu.setTransitionOutAnimator(out);
    }

    public static void setMenuTransition(Transition in, Transition out) {
        mainMenu.setMenuTransitions(in, out);
        UIManager.getInstance().getLookAndFeel().setDefaultMenuTransitionIn(in);
        UIManager.getInstance().getLookAndFeel().setDefaultMenuTransitionOut(out);
    }

    public static void setComponentTransition(Transition t) {
        if(t != null){
            mainMenu.setSmoothScrolling(false);
        }
        componentTransitions = t;
    }

    public static Transition getComponentTransition() {
        return componentTransitions;
    }

    void setMainForm(/*Resources r*/) throws IOException {
//        UIManager.getInstance().setResourceBundle(r.getL10N("localize", "en"));

        // application logic determins the number of columns based on the screen size
        // this is why we need to be aware of screen size changes which is currently
        // only received using this approach
        mainMenu = new MainScreenForm(this, "LWUIT Demo");
        
        int width = Display.getInstance().getDisplayWidth(); //get the display width 

        elementWidth = 0;

        mainMenu.setTransitionOutAnimator(CommonTransitions.createFade(400));

        Image[] selectedImages = new Image[DEMOS.length];
        Image[] unselectedImages = new Image[DEMOS.length];

        final ButtonActionListener bAListner = new ButtonActionListener();
        for (int i = 0; i < DEMOS.length; i++) {
            Image temp = Image.createImage("/images/icons/" + DEMOS[i].getName() + "_sel.png");
            selectedImages[i] = temp;
            unselectedImages[i] = Image.createImage("/images/icons/" + DEMOS[i].getName() + "_unsel.png");
            final Button b = new Button(DEMOS[i].getName(), unselectedImages[i]) {

                public Image getPressedIcon() {
                    Image i = getIcon();
                    return i.scaled((int) (i.getWidth() * 0.8), (int) (i.getHeight() * 0.8));
                }
            };
            b.setRolloverIcon(selectedImages[i]);
            Style s = b.getStyle();
            s.setBorder(null);
            s.setBgTransparency(0);
            s.setBgColor(0xffffff);
            b.setAlignment(Label.CENTER);
            b.setTextPosition(Label.BOTTOM);
            mainMenu.addComponent(b);
            b.addActionListener(bAListner);
            b.addFocusListener(new FocusListener() {

                public void focusGained(Component cmp) {
                    if (componentTransitions != null) {
                        mainMenu.replace(b, b, componentTransitions);
                    }
                }

                public void focusLost(Component cmp) {
                }
            });

            demosHash.put(b, DEMOS[i]);
            elementWidth = Math.max(b.getPreferredW(), elementWidth);
        }

        //Calculate the number of columns for the GridLayout according to the 
        //screen width
        cols = width / elementWidth;
        int rows = DEMOS.length / cols;
        mainMenu.setLayout(new GridLayout(rows, cols));

        //mainMenu.addComponent(BorderLayout.CENTER, mainContainer);

        mainMenu.addCommand(exitCommand);
        mainMenu.addCommand(aboutCommand);
        mainMenu.addCommand(dragModeCommand);
        mainMenu.addCommand(runCommand);

        mainMenu.addCommandListener(this);
        mainMenu.show();
    }

    /**
     * Invoked when a command is clicked. We could derive from Command but that would 
     * require 3 separate classes.
     */
    public void actionPerformed(ActionEvent evt) {
        Command cmd = evt.getCommand();
        switch (cmd.getId()) {
            case RUN_COMMAND:
                currentDemo = ((Demo) (demosHash.get(mainMenu.getFocused())));
                currentDemo.run(backCommand, this);
                break;
            case EXIT_COMMAND:
                notifyDestroyed();
                break;
            case BACK_COMMAND:
                currentDemo.cleanup();
                mainMenu.refreshTheme();
                mainMenu.show();

                // for series 40 devices
                System.gc();
                System.gc();
                break;
            case ABOUT_COMMAND:
                Form aboutForm = new Form("About");
                aboutForm.setScrollable(false);
                aboutForm.setLayout(new BorderLayout());
                TextArea aboutText = new TextArea(getAboutText(), 5, 10);
                aboutText.setEditable(false);
                aboutForm.addComponent(BorderLayout.CENTER, aboutText);
                aboutForm.addCommand(new Command("Back") {
                    public void actionPerformed(ActionEvent evt) {
                        mainMenu.show();
                    }
                });
                aboutForm.show();
                break;
            case DRAG_MODE_COMMAND: 
                mainMenu.setDragMode(true);
                mainMenu.removeCommand(dragModeCommand);
                mainMenu.addCommand(scrollModeCommand);
                break;
            case SCROLL_MODE_COMMAND: 
                mainMenu.setDragMode(false);
                mainMenu.removeCommand(scrollModeCommand);
                mainMenu.addCommand(dragModeCommand);
                break;
        }
    }

    private String getAboutText() {
        return "LWUIT Demo shows the main features for developing a User " +
                "Interface (UI) in Java ME. " +
                "This demo contains inside additional different sub-demos " +
                "to demonstrate key features, where each one can be reached " +
                "from the main screen. For more details about each sub-demo, " +
                "please visit the demo help screen. For more details, please " +
                "contact us at lwuit-comments@sun.com.";
    }

    private class ButtonActionListener implements ActionListener {

        public void actionPerformed(ActionEvent evt) {
            currentDemo = ((Demo) (demosHash.get(evt.getSource())));
            currentDemo.run(backCommand, UIDemoMIDlet.this);
        }
    }

    /**
     * Simple progress indicator component that fills out the progress made.
     * Progress is assumed to always be horizontal in this widget
     */
    private static class Progress extends Component {

        private byte percent;
        private Image unfilled;
        private Image filled;

        /**
         * The default constructor uses internal rendering to draw the progress
         */
        public Progress() {
            setFocusable(false);
        }

        /**
         * Allows indicating the progress using a filled/unfilled images.
         * The unfilled image is always drawn and the filled image is drawn on top with
         * clipping to indicate the amount of progress made.
         *
         * @param unfilled an image containing the progress bar without any of its
         * content being filled (with the progress color)
         * @param filled an image identicall to unfilled in every way except that progress
         * is completed in this bar.
         */
        public Progress(Image unfilled, Image filled) {
            this();
            this.unfilled = unfilled;
            this.filled = filled;
        }

        /**
         * Indicate to LWUIT the component name for theming in this case "Progress"
         */
        public String getUIID() {
            return "Progress";
        }

        /**
         * Indicates the percent of progress made
         */
        public byte getProgress() {
            return percent;
        }

        /**
         * Indicates the percent of progress made, this method is thread safe and
         * can be invoked from any thread although discression should still be kept
         * so one thread doesn't regress progress made by another thread...
         */
        public void setProgress(byte percent) {
            this.percent = percent;
            repaint();
        }

        /**
         * Return the size we would generally like for the component
         */
        protected Dimension calcPreferredSize() {
            if (filled != null) {
                return new Dimension(filled.getWidth(), filled.getHeight());
            } else {
                // we don't really need to be in the font height but this provides
                // a generally good indication for size expectations
                return new Dimension(Display.getInstance().getDisplayWidth(),
                        Font.getDefaultFont().getHeight());
            }
        }

        /**
         * Paint the progress indicator
         */
        public void paint(Graphics g) {
            int width = (int) ((((float) percent) / 100.0f) * getWidth());
            if (filled != null) {
                if (filled.getWidth() != getWidth()) {
                    filled = filled.scaled(getWidth(), getHeight());
                    unfilled = unfilled.scaled(getWidth(), getHeight());
                }

                // draw based on two user supplied images
                g.drawImage(unfilled, getX(), getY());
                g.clipRect(getX(), getY(), width, getHeight());
                g.drawImage(filled, getX(), getY());
            } else {
                // draw based on simple graphics primitives
                Style s = getStyle();
                g.setColor(s.getBgColor());
                int curve = getHeight() / 2 - 1;
                g.fillRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, curve, curve);
                g.setColor(s.getFgColor());
                g.drawRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, curve, curve);
                g.clipRect(getX(), getY(), width - 1, getHeight() - 1);
                g.setColor(s.getBgColor());
                g.fillRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, curve, curve);
                g.setColor(s.getFgColor());
                g.drawRoundRect(getX(), getY(), getWidth() - 1, getHeight() - 1, curve, curve);
            }
        }
    }
    
    
}
